/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2007，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	HDataset.java
 * 模块说明：	
 * 修改历史：
 * Aug 24, 2007 - lxm - 创建。
 */
package com.hd123.rumba.jsp.table;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.util.AccessMode;

/**
 * 数据集对象，设计用于与浏览器页面进行数据交换。
 * 
 * @author lxm
 * 
 */
public class HDataset {

  /** 字段定义列表 */
  private List<HFieldDef> fieldDefs = new ArrayList<HFieldDef>();
  /** 数据记录列表 */
  private List<HRecord> records = new ArrayList<HRecord>();

  /**
   * 清除所有的字段定义。
   * 
   */
  public void clearFieldDefs() {
    if (fieldDefs == null)
      fieldDefs = new ArrayList<HFieldDef>();
    else
      fieldDefs.clear();
  }

  /**
   * 添加一个字段定义。
   * 
   * @param fieldName
   *          指定字段名。
   * @param dataType
   *          指定字段数据类型。
   * @param permissionId
   *          指定字段权限ID。
   * @return 返回创建的字段定义对象。
   * @throws BusinessException
   */
  public HFieldDef appendFieldDef(String fieldName, String dataType, String permissionId)
      throws BusinessException {
    assert this.fieldDefs != null;
    if (this.getFieldDefByName(fieldName) != null)
      throw new BusinessException("字段名(" + fieldName + ")重复。");
    if (dataType == null)
      throw new BusinessException("必须的参数dataType不可为null。");

    HFieldDef fieldDef = new HFieldDef();
    fieldDef.setDataset(this);
    fieldDef.setName(fieldName);
    fieldDef.setDataType(dataType);
    fieldDef.setPermissionId(permissionId);
    this.fieldDefs.add(fieldDef);
    return fieldDef;
  }

  /**
   * 取得所有的字段定义。
   * 
   * @return 字段定义列表。
   */
  public List<HFieldDef> getFieldDefs() {
    return this.fieldDefs;
  }

  /**
   * 根据字段名取得字段定义对象。
   * 
   * @param fieldName
   *          指定字段名。
   * @return 返回字段定义对象。若找不到则返回null。
   */
  public HFieldDef getFieldDefByName(String fieldName) {
    assert this.fieldDefs != null;
    HFieldDef fieldDef = null;
    for (HFieldDef fd : this.fieldDefs)
      if (fd.getName().equals(fieldName)) {
        fieldDef = fd;
        break;
      }
    return fieldDef;
  }

  /**
   * 清除所有数据记录。
   * 
   */
  public void clear() {
    if (this.records == null)
      this.records = new ArrayList<HRecord>();
    else
      this.records.clear();
  }

  /**
   * 添加一个数据记录。
   * 
   * @return 返回新增的数据记录。
   */
  public HRecord append() {
    assert this.records != null;
    HRecord record = new HRecord();
    record.setDataset(this);
    this.records.add(record);
    return record;
  }

  /**
   * 插入一个数据记录。
   * 
   * @param beforeRecord
   *          指定一个已经存在的数据记录，新记录将位于这个记录之前。
   * @return 返回新创建的数据记录对象。
   * @throws BusinessException
   */
  public HRecord insert(HRecord beforeRecord) throws BusinessException {
    assert this.records != null;
    int idx = this.records.indexOf(beforeRecord);
    if (idx < 0)
      throw new BusinessException("指定的数据记录beforeRecord不属于当前数据集。");
    HRecord record = new HRecord();
    record.setDataset(this);
    this.records.add(idx, record);
    return record;
  }

  /**
   * 插入一个数据记录。
   * 
   * @param index
   *          指定新记录所在索引。
   * @return 返回新创建的数据记录对象。
   */
  public HRecord insert(int index) {
    assert this.records != null;
    HRecord record = new HRecord();
    record.setDataset(this);
    this.records.add(index, record);
    return record;
  }

  /**
   * 删除指定的数据记录。
   * 
   * @param record
   *          指定被删除的数据记录对象。
   * @return 返回是否删除成功。
   */
  public boolean remove(HRecord record) {
    assert this.records != null;
    return this.records.remove(record);
  }

  /**
   * 删除指定的数据记录。
   * 
   * @param index
   *          指定被删除的数据记录的索引。
   * @return 返回被删除的数据记录对象。
   */
  public HRecord remove(int index) {
    assert this.records != null;
    return this.records.remove(index);
  }

  /**
   * 序列化所包含的数据。
   * 
   * @param am
   *          指定访问模式对象。
   * @return
   * @throws BusinessException
   */
  public String serialize(AccessMode am) throws BusinessException {
    AccessMode am2 = am == null ? new AccessMode() : am;
    try {
      DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      Document doc = builder.newDocument();
      this.writeToXml(doc, am2);
      Transformer t = TransformerFactory.newInstance().newTransformer();
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      t.transform(new DOMSource(doc), new StreamResult(out));
      return out.toString();
    } catch (Exception e) {
      throw new BusinessException(e);
    }
  }

  /**
   * 反序列化数据。
   * 
   * @param data
   *          指定的序列化数据格式。
   * @throws BusinessException
   */
  public void deserialize(String data) throws BusinessException {
    try {
      DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      ByteArrayInputStream in = new ByteArrayInputStream(data.getBytes());
      Document doc = builder.parse(in);
      this.readFromXml(doc);
    } catch (Exception e) {
      throw new BusinessException(e);
    }
  }

  private void writeToXml(Document doc, AccessMode am) {
    Element eDataset = doc.createElement("dataset");

    Element eFieldDefs = doc.createElement("fieldDefs");
    for (HFieldDef fieldDef : this.fieldDefs)
      fieldDef.writeToXml(doc, eFieldDefs, am);
    eDataset.appendChild(eFieldDefs);

    Element eRecords = doc.createElement("records");
    for (HRecord record : this.records)
      record.writeToXml(doc, eRecords, am);
    eDataset.appendChild(eRecords);

    doc.appendChild(eDataset);
  }

  private void readFromXml(Document doc) throws BusinessException {
    Element eDataset = doc.getDocumentElement();

    this.clearFieldDefs();
    NodeList nlFieldDefs = eDataset.getElementsByTagName("fieldDefs");
    if (nlFieldDefs.getLength() > 0) {
      Element eFieldDefs = (Element) nlFieldDefs.item(0);
      NodeList nlFieldDef = eFieldDefs.getElementsByTagName("fieldDef");
      for (int i = 0; i < nlFieldDef.getLength() - 1; i++) {
        Element eFieldDef = (Element) nlFieldDef.item(i);
        HFieldDef fieldDef = new HFieldDef();
        fieldDef.setDataset(this);
        fieldDef.readFromXml(eFieldDef);
        this.fieldDefs.add(fieldDef);
      }
    }
    
    this.clear();
    NodeList nlRecords = eDataset.getElementsByTagName("records");
    if (nlRecords.getLength() > 0) {
      Element eRecords = (Element) nlRecords.item(0);
      NodeList nlRecord = eRecords.getElementsByTagName("record");
      for (int i = 0; i < nlRecord.getLength() - 1; i++) {
        Element eRecord = (Element) nlRecord.item(i);
        HRecord record = new HRecord();
        record.setDataset(this);
        record.readFromXml(eRecord);
        this.records.add(record);
      }
    }
  }

}
